﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;

namespace WPFSample.CalculateInHexSample
{
    class CalculateInHex : Window
    {
        RoundedButton btnDisplay;
        ulong numDisplay;
        ulong numFirst;
        bool bNewNumber = true;
        char chOperation = '=';

        public CalculateInHex()
        {
            this.Title = "Calculate in Hex";
            this.SizeToContent = SizeToContent.WidthAndHeight;
            this.ResizeMode = ResizeMode.CanMinimize;

            Grid grid = new Grid();
            grid.Margin = new Thickness(4);
            this.Content = grid;

            for (int i = 0; i < 5; i++)
            {
                ColumnDefinition col = new ColumnDefinition();
                col.Width = GridLength.Auto;
                grid.ColumnDefinitions.Add(col);
            }

            for (int i = 0; i < 7; i++)
            {
                RowDefinition row = new RowDefinition();
                row.Height = GridLength.Auto;
                grid.RowDefinitions.Add(row);
            }

            string[] strButtons = {"0",
                                    "D","E","F","+","&",
                                    "A","B","C","-","|",
                                    "7","8","9","*","^",
                                    "4","5","6","/","<<",
                                    "1","2","3","%",">>",
                                    "0","Back","Equals"};
            int iRow = 0, iCol = 0;
            foreach (string str in strButtons)
            {
                RoundedButton btn = new RoundedButton()
                {
                    Focusable = false,
                    Height = 32,
                    Margin = new Thickness(4)
                };
                btn.Click += this.ButtonOnClick;

                TextBlock txt = new TextBlock();
                txt.Text = str;
                btn.Child = txt;

                grid.Children.Add(btn);
                Grid.SetRow(btn, iRow);
                Grid.SetColumn(btn, iCol);

                if (iRow == 0 && iCol == 0)
                {
                    btnDisplay = btn;
                    btn.Margin = new Thickness(4, 4, 4, 6);
                    Grid.SetColumnSpan(btn, 5);
                    iRow = 1;
                }
                else if (iRow == 6 && iCol > 0)
                {
                    Grid.SetColumnSpan(btn, 2);
                    iCol += 2;
                }
                else
                {
                    btn.Width = 32;
                    if (0 == (iCol = (iCol + 1) % 5))
                    {
                        iRow++;
                    }
                }
            }
        }

        private void ButtonOnClick(object sender, RoutedEventArgs e)
        {
            RoundedButton btn = e.Source as RoundedButton;
            if (btn == null)
            {
                return;
            }

            string strButton = (btn.Child as TextBlock).Text;
            char chButton = strButton[0];
            if (strButton == "Equals")
            {
                chButton = '=';
            }

            if (btn == btnDisplay)
            {
                numDisplay = 0;
            }
            else if (strButton == "Back")
            {
                numDisplay /= 16;
            }
            else if (char.IsLetterOrDigit(chButton))
            {
                if (bNewNumber)
                {
                    this.numFirst = this.numDisplay;
                    this.numDisplay = 0;
                    bNewNumber = false;
                }

                if (this.numDisplay <= ulong.MaxValue >> 4)
                {
                    this.numDisplay = 16 * this.numDisplay + (ulong)(chButton - (char.IsDigit(chButton) ? '0' : 'A' - 10));
                }
            }
            else
            {
                if (!bNewNumber)
                {
                    switch (chOperation)
                    {
                        case '=':
                            break;
                        case '+':
                            this.numDisplay = this.numFirst + this.numDisplay;
                            break;
                        case '-':
                            this.numDisplay = this.numFirst - this.numDisplay;
                            break;
                        case '*':
                            this.numDisplay = this.numFirst * this.numDisplay;
                            break;
                        case '&':
                            this.numDisplay = this.numFirst & this.numDisplay;
                            break;
                        case '|':
                            this.numDisplay = this.numFirst | this.numDisplay;
                            break;
                        case '^':
                            this.numDisplay = this.numFirst ^ this.numDisplay;
                            break;
                        case '<':
                            this.numDisplay = this.numFirst << (int)this.numDisplay;
                            break;
                        case '>':
                            this.numDisplay = this.numFirst >> (int)this.numDisplay;
                            break;
                        case '/':
                            this.numDisplay = this.numDisplay != 0 ? this.numFirst / this.numDisplay : ulong.MaxValue;
                            break;
                        case '%':
                            this.numDisplay = this.numDisplay != 0 ? this.numFirst % this.numDisplay : ulong.MaxValue;
                            break;
                        default:
                            this.numDisplay = 0;
                            break;
                    }
                }

                bNewNumber = true;
                chOperation = chButton;

                TextBlock text = new TextBlock();
                text.Text = string.Format("{0:X}", this.numDisplay);
                this.btnDisplay.Child = text;
            }
        }

        protected override void OnTextInput(TextCompositionEventArgs e)
        {
            base.OnTextInput(e);
            if (e.Text.Length == 0)
            {
                return;
            }

            char chKey = char.ToUpper(e.Text[0]);
            foreach (UIElement child in (this.Content as Grid).Children)
            {
                RoundedButton btn = child as RoundedButton;
                string strButton = (btn.Child as TextBlock).Text;
                if ((chKey == strButton[0] && btn != btnDisplay &&
                    strButton != "Equals" && strButton != "Back") ||
                        (chKey == '=' && strButton == "Equals") ||
                        (chKey == '\r' && strButton == "Equals") ||
                        (chKey == '\b' && strButton == "Back") ||
                        (chKey == '\x1B' && btn == btnDisplay))
                {
                    RoutedEventArgs argsClick = new RoutedEventArgs(RoundedButton.ClickEvent, btn);
                    btn.RaiseEvent(argsClick);

                    btn.IsPressed = true;

                    DispatcherTimer tmr = new DispatcherTimer();
                    tmr.Interval = TimeSpan.FromMilliseconds(100);
                    tmr.Tag = btn;
                    tmr.Tick += this.TimerOnTick;
                    tmr.Start();
                    e.Handled = true;
                }
            }
        }

        private void TimerOnTick(object sender, EventArgs e)
        {
            DispatcherTimer tmr = sender as DispatcherTimer;
            RoundedButton btn = tmr.Tag as RoundedButton;
            btn.IsPressed = false;
            tmr.Stop();
            tmr.Tick -= this.TimerOnTick;
        }
    }
}
